package com.x52im.rainbowchat.http.logic.logic;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.eva.epc.common.util.CommonUtils;
import com.eva.framework.Processor;
import com.eva.framework.dbpool.DBDepend;
import com.eva.framework.dbpool.DBShell;
import com.eva.framework.utils.EndsExceptionFactory;
import com.x52im.rainbowchat.http.logic.AbstractJobDispatcher;
import com.x52im.rainbowchat.http.logic.LogicProcessor2;

import java.util.Vector;

public class CommonQueryJobDispatcher extends AbstractJobDispatcher {
    DBShell db = LogicProcessor2.db;

    /**
     * 维护通用数据查询接口的管理.
     *
     * @param action_id 维护类型，SysConstant中的操作类型常量
     * @param newData   客端发送过来的本次修改新数据(可能为空，理论上与oldData不会同时空）
     * @param oldData   客端发送过来的本次修改前的老数据(可能为空，理论上与newData不会同时空）
     * @param userName  操作者的用户名
     * @return 返回给客户端的对象
     */
    @Override
    public String dispatch(int action_id, Object newData, Object oldData, Processor.User user) throws Exception {
        String newDataJSON = newData.toString();
        Vector sqls = new Vector();
        Vector values = new Vector();

        switch (action_id) {
            // 【接口1008-26-7】: 具体说明，详见“RainbowChat4.0-HttpRest接口手册.pdf”！
            // 用途说明：用于查询（构造）客户端界面上首页“消息”列表；
            // 数据说明：本列表的数据，取自被查询用户与所有聊天对象的最近一次聊天记录（消息内容、消息类型、消息时间等）；
            // 适用范围：当对接RainbowChat-Web产品时，本接口将被用于Web端（虽然移动端等也可以用，但没有必要，原因为
            //          Android或iOS端有本地缓存数据，直接本地构建这个数据列表即可，无需连服务端查询）；
            case ACTION_APPEND1: {
                // 客户端传过来的数据
                JSONObject nwObj = JSONObject.parseObject(newDataJSON);

                // 被查询者的uid
                String uid = nwObj.getString("uid");
                // 数据查询范围的起始时间（此参数为空表示查询时不区分时间范围），形如：“2019-01-01 10:02:02”
                String startTime = nwObj.getString("starttime");

                // 是否需要查询时间范围条件
                boolean hasTimeCondition = (!CommonUtils.isStringEmpty(startTime, true));
                // 额外的时间日期SQL过滤条件
                String timeFilter = (hasTimeCondition
                        ? " (msg_time BETWEEN '" + startTime + "' and " + DBDepend.getDefaultDatetimeFunc() + ")"
                        : "");

                StringBuilder sb = new StringBuilder();

                //## 首先构建要查询数据的SQL语句：
                //   此语句由多层子查询构成，因为要在原先简单的数据库表结构的基础上实现这种
                //   较复杂的数据查询逻辑，且需考虚查询性能的情况下，子查询是必须要使用的。

                /* 用所有“我”与对方或我参与的群聊的最近一次聊天消息记录的id，再级连查出这些聊天记录的完整内容 */
                // 提示：以下是一个非常复杂的嵌套子查询SQL语句，目的是力求一个完整SQL查出需要的数据，减少数据库查询次数
                sb.append("SELECT ")
                        .append("	  XX.fid")           // 结果字段1：聊天对象的uid

                        // 结果字段2：聊天对象的昵称
//				.append("	, ZZ.nickname" )      // v7.3前的写法
//				.append("	, (select "+DBDepend.getNVLFunc("remark", "ZZ.nickname")+" from missu_roster where user_uid='400071' and friend_user_uid=ZZ.user_uid) as remark " )
                        .append("	, " + DBDepend.getNVLFunc("(select remark from missu_roster where user_uid=" + uid + " and friend_user_uid=ZZ.user_uid)", "ZZ.nickname") + " as remark ")

                        .append("	, YY.msg_type")      // 结果字段3：聊天消息类型，见MsgBodyRoot类中的定义
                        .append("	, YY.msg_content")   // 结果字段4：聊天消息内容
                        .append("	, YY.msg_time")      // 结果字段5：聊天时间
                        .append("	, YY.msg_time2")     // 结果字段6：聊天时间（java时间戳长整形）
                        .append("	, YY.src_uid ")       // 结果字段7：此行聊天记录的源uid（保留字段，暂未用到）
                        /* 结果字段8：判断聊天对象是否是“我”的好友（当VV.user_uid为null时即表示无匹配记录），返回值是0（否）或1（是）哦 */
//				.append("	, (case VV.user_uid when null then 0 else 1 end) as isFriends ")
                        // 注意：sql server中，遇到null值时，如果case xxxx when null这种写法，则即便 xxxx是null,得到的结果也是1，这很可能是sql server的bug！
                        .append("	, (case when VV.user_uid is null then 0 else 1 end) as isFriends ")
//				.append("	/*, case when XX.fid=YY.src_uid then 1 else 0 end as isCommingMsg*/ /* 表示是对方发给“我”的消息，否则是“我”发给“他”的 */")
                        .append("	, YY.chat_type ")      // 结果字段9：聊天模式，详见 MsgBodyRoot类常量
                        .append("	, CASE YY.chat_type WHEN 2 then dest_uid else null end as g_id ")// 结果字段10：群id（群聊消息时有意义）
                        .append("	, WW.g_name ")         // 结果字段11：群名称（群聊消息时有意义）
                        .append("FROM ")
                        .append("(")
                        /* 再将“我”的所有聊天记录按照聊天者进行分组，并对每个分组按聊天记录id进行逆序排序，并取出每个分组中id最大的记录（即我与每个人最近一次的聊天消息） */
                        .append("	SELECT X.fid AS fid,max(X.collect_id) AS collect_id FROM ")
                        .append("	(")
                        /* [单聊消息]先将所有“我”主动聊天的人，或者“我”被动聊天的人的消息，进行并集，合并成我的所有聊天记录 */
                        .append("		SELECT dest_uid AS fid,collect_id FROM missu_user_msgs_collect WHERE src_uid=" + uid + " AND chat_type IN(0,1) ")
                        .append("		UNION ")
                        .append("		SELECT src_uid AS fid,collect_id FROM missu_user_msgs_collect WHERE dest_uid=" + uid + " AND chat_type IN(0,1) ")
                        .append("		UNION")
                        /* [群聊消息]查询我所在所有群组收到的消息（本SQL的where查询条件如用IN则更简单，但EXISTS语句性能更好，所以用之） */
                        .append("		SELECT dest_uid AS fid,collect_id FROM missu_user_msgs_collect AAA ")
                        .append("		WHERE EXISTS (")
                        .append("			select 1 from (")
                        .append("				select A.g_id FROM (select g_id from group_members where user_uid=" + uid + ") A join group_base B on A.g_id=B.g_id and B.g_status=1 ")
                        .append("			)AA where AA.g_id=dest_uid ")
                        .append("		) ")
                        // @since 4.5b20190401: 额外条件实现聊天记录只查到该用户能看到的聊天时间之后（用于防止用户能看到入群前的聊天记录，以及实现首页“消息”中“删除”消息的功能）
                        .append("		and EXISTS(select 1 from group_members B where B.g_id=AAA.dest_uid and B.user_uid=" + uid + " and (AAA.msg_time >= B.msg_time_start or B.msg_time_start is null)) ")
                        .append("		and chat_type=2 ")
                        .append("	) X ")
                        .append("	GROUP BY X.fid ")
//				.append("	/*ORDER BY X.collect_id desc*/")
                        .append(") XX ")
                        .append("JOIN missu_user_msgs_collect YY ON XX.collect_id = YY.collect_id ")
                        .append("LEFT JOIN missu_users ZZ ON XX.fid = ZZ.user_uid ")
                        .append("LEFT JOIN group_base WW ON (CASE YY.chat_type WHEN 2 then dest_uid else null end)=WW.g_id ")
                        .append("LEFT JOIN missu_roster VV ON VV.user_uid=" + uid + " AND XX.fid=VV.friend_user_uid ")
                        .append("WHERE " + timeFilter)
                        .append("ORDER BY YY.msg_time DESC ");

                Vector<Vector> vecs = db.queryData(sb.toString());

                return JSON.toJSONString(vecs);

            }

            // 【接口1008-26-8】:查询聊天记录（已用于：Web端）。具体说明，详见“RainbowChat4.0-HttpRest接口手册.pdf”！
            //  支持：一对一聊天记录的返回（即“我”与指定uid用户的记录）、群聊记录的返回
            //  · 用途说明：用于从服务端查询最近一段时间的聊天记录；
            //  · 数据说明：因服务端存在超期聊天记录将被转储的功能，因而本接口的数据只包含转储超时期限内的；
            //  · 适用范围：当对接RainbowChat-Web产品时，本接口将被用于Web端（虽然移动端等也可以用，但没有
            //             必要，原因为Android或iOS端有本地缓存数据，直接本地查询即可，无需连服务端查询）。
            case ACTION_APPEND2: {
                // 客户端传过来的数据
                JSONObject nwObj = JSONObject.parseObject(newDataJSON);

                String gid = nwObj.getString("gid");   // 查群聊记录必填参数：要查询的群id

                String luid = nwObj.getString("luid"); // 查单聊或群聊记录必填参数：local uid（即“我”的uid）

                String ruid = nwObj.getString("ruid"); // 查单聊记录选填参数：remote uid（即“对方”的uid）

                // true表示当前要查询的是群聊消息记录，否则表示要查询的一对一聊天记录
                boolean isQueryGroupChatting = (!CommonUtils.isStringEmpty(gid, true));

                String orderby = nwObj.getString("orderby");     // 排序方式： 1 表示按消息时间DESC逆序，0 表示按消息时间ASC顺序排序

                String startTime = nwObj.getString("starttime"); // 聊天记录查询范围的起始时间（为空表示不区分时间范围），形如：“2019-01-01 10:02:02”
                String endTime = nwObj.getString("endtime");     // 聊天记录查询范围的结束时间（为空表示查询截止当前时间），形如：“2019-01-01 10:02:02”

                // 是否需要查询时间范围条件
                boolean hasTimeCondition = (!CommonUtils.isStringEmpty(startTime, true));

                StringBuilder sb = new StringBuilder();

                String filter = "1=1";

                // 查询过滤条件
                if (isQueryGroupChatting) {
                    // 群聊查询条件
                    filter = "(chat_type=2 and dest_uid=" + gid + " and " +
                            // @since 4.5b20190401: 额外条件实现聊天记录只查到该用户能看到的聊天时间之后（用于防止用户能看到入群前的聊天记录，以及实现首页“消息”中“删除”消息的功能）
                            "	EXISTS(select 1 from group_members b where b.g_id='" + gid + "' and b.user_uid='" + luid + "' and (a.msg_time >= b.msg_time_start or b.msg_time_start is null))" +
                            ")";
                } else {
                    // 单聊查询条件
                    filter = "((src_uid='" + luid + "' and dest_uid='" + ruid
                            + "') or (src_uid='" + ruid + "' and dest_uid='" + luid + "'))";
                }

                // 额外的时间日期过滤条件
                String timeFilter = (hasTimeCondition
                        ? "and (msg_time BETWEEN '" + startTime + "' and " + (CommonUtils.isStringEmpty(endTime, true) ? DBDepend.getDefaultDatetimeFunc() : "'" + endTime + "'") + ")"
                        : "");

                // 组合成完整的sql语句
                sb.append("select ")
                        .append("	src_uid, dest_uid, chat_type, msg_type, msg_content")
                        // 将msg_time2字段的无时区时间戳传给客户端，以便客户端按其所有时区显示为正确的时间
                        .append("	,msg_time2 ")
//					.append("	,DATE_FORMAT(msg_time,'"+DATETIME_FORMAT_FROM_MYSQL_YYYY_MM_DD_HHMMSS+"') as msg_time")
                        .append("	,fingerprint ")
                        .append("	,parent_fp ")
                        .append("from missu_user_msgs_collect a ")
                        .append("where " + filter + " " + timeFilter)
                        .append("order by collect_id " + ("1".equals(orderby) ? "desc" : "asc"));// 使用collect_id排序比msg_time更精确（因为理论上存在时间重复的可能性）

                Vector<Vector> vecs = db.queryData(sb.toString());

                return JSON.toJSONString(vecs);
            }

            default:
                throw EndsExceptionFactory.INVALID_ACTION_ID(action_id);
        }
    }
}
